package com.threeox.permissions;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.threeox.permissions.annotation.OnShowCause;
import com.threeox.permissions.annotation.PermissionDecline;
import com.threeox.permissions.annotation.PermissionSucceeded;
import com.threeox.utillibrary.util.EmptyUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @ClassName: PermissionsDispatcher
 *
 * @Description: TODO(权限管理的分发类)
 *
 * @author 赵屈犇
 *
 * @date 创建时间: 2017/8/21 13:24
 *
 * @version 1.0
 *
 */
@TargetApi(Build.VERSION_CODES.M)
public class PermissionsDispatcher {

    public static final int REQUESTCODEPERMISSIONS = 0x000001;// 默认的权限管理Code
    private final String TAG = PermissionsDispatcher.class.getName();

    private Object mObject;
    private Activity mActivity;
    private Class<? extends Object> mClasses;

    private Map<Integer, PermissionsMessage> mPermissionCause = new HashMap<Integer, PermissionsMessage>();// 显示需要权限原因的
    private Map<Integer, PermissionsMessage> mPermissionSucceed = new HashMap<Integer, PermissionsMessage>();// 权限赋予成功
    private Map<Integer, PermissionsMessage> mPermissionDecline = new HashMap<Integer, PermissionsMessage>();// 权限被拒绝

    private PermissionsDispatcher(){}

    public static PermissionsDispatcher init(Activity activity, Object object) {
        return new PermissionsDispatcher(activity, object);
    }

    private PermissionsDispatcher(Activity activity, Object object) {
        try {
            this.mObject = object;
            this.mActivity = activity;
            this.mClasses = object.getClass();
            initPermissionConfig(mClasses.getDeclaredMethods());
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * 初始化权限的配置参数
     *
     * @param methods
     */
    private void initPermissionConfig(Method[] methods) {
        if (EmptyUtils.isNotEmpty(methods)) {
            for (Method method: methods) {
                try {
                    boolean isShowCause = method.isAnnotationPresent(OnShowCause.class);
                    if (isShowCause) {
                        OnShowCause showCause = method.getAnnotation(OnShowCause.class);
                        int[] requestCodes = showCause.requestCodes();
                        if (EmptyUtils.isNotEmpty(requestCodes)) {
                            for (int requestCode: requestCodes) {
                                putPermissionCause(showCause, method, requestCode);
                            }
                        } else {
                            putPermissionCause(showCause, method, showCause.requestCode());
                        }
                    }
                    boolean isSucceed = method.isAnnotationPresent(PermissionSucceeded.class);
                    if (isSucceed) {
                        PermissionSucceeded succeeded = method.getAnnotation(PermissionSucceeded.class);
                        int[] requestCodes = succeeded.requestCodes();
                        if (EmptyUtils.isNotEmpty(requestCodes)) {
                            for (int requestCode: requestCodes) {
                                putPermissionSucceed(succeeded, method, requestCode);
                            }
                        } else {
                            putPermissionSucceed(succeeded, method, succeeded.requestCode());
                        }
                    }
                    boolean isDecline = method.isAnnotationPresent(PermissionDecline.class);
                    if (isDecline) {
                        PermissionDecline decline = method.getAnnotation(PermissionDecline.class);
                        int[] requestCodes = decline.requestCodes();
                        if (EmptyUtils.isNotEmpty(requestCodes)) {
                            for (int requestCode: requestCodes) {
                                putPermissionDecline(decline, method, requestCode);
                            }
                        } else {
                            putPermissionDecline(decline, method, decline.requestCode());
                        }
                    }
                } catch (Exception e) {
                    Log.e(TAG, "initPermissionConfig:报错啦:" + e.getMessage());
                }
            }
        }
    }

    /**
     * 根据requestCode 执行函数
     *
     * @param requestCode
     * @param params
     */
    public PermissionsDispatcher invokeMethod(int requestCode, Object... params) {
        PermissionsMessage permissionsMessage = mPermissionSucceed.get(requestCode);
        if (permissionsMessage != null) {
            String[] permissions = findDeniedPermissions(permissionsMessage.getPermissions());
            if (EmptyUtils.isNotEmpty(permissions)) {// 需要请求权限
                permissionsMessage.setMethodParams(params);
                PermissionsRequest request = new PermissionsRequest(mActivity, permissions, requestCode);
                if (permissionsMessage.isShowCause()) {// 需要显示原因
                    PermissionsMessage causeMessage = mPermissionCause.get(requestCode);
                    Object result = null;
                    if (causeMessage != null) {
                        result = invokeMethod(causeMessage.getMethod(), requestCode, permissions, request);
                    } else {
                        request.requestPermissions();
                    }
                    if (result != null && result instanceof Boolean) {
                        permissionsMessage.setShowCause((Boolean) result);
                    } else {
                        permissionsMessage.setShowCause(false);
                    }
                } else {
                    request.requestPermissions();
                }
            } else {
                try {
                    Method method = permissionsMessage.getMethod();
                    method.setAccessible(true);
                    method.invoke(mObject, params);
                } catch (Exception e) {
                    Log.e(TAG, "调用函数失败" + e.getMessage());
                }
            }
        } else {
            Log.d(TAG, "根据requestCode没有找到请求函数,请检查requestCode正确性!");
        }
        return this;
    }


    /**
     * 获取权限集中需要申请权限的列表
     *
     * @param permissions
     * @return
     *
     * checkSelfPermission方法是在用来判断是否app已经获取到某一个权限
     * shouldShowRequestPermissionRationale方法用来判断是否
     * 显示申请权限对话框，如果同意了或者不在询问则返回false
     */
    private String[] findDeniedPermissions(String[] permissions) {
        List<String> needRequestPermissonList = new ArrayList<String>();
        for (String permission : permissions) {
            int checkSelfPermission = PackageManager.PERMISSION_DENIED;
            try {
                checkSelfPermission = ActivityCompat.checkSelfPermission(mActivity, permission);
            } catch (Exception e) {
                Log.e(TAG, "检测权限失败:" + e.getMessage());
            }
            if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {// 拒绝让APP使用权限
                needRequestPermissonList.add(permission);
            } else {
                if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, permission)) {// 用户彻底禁止用户权限申请
                    needRequestPermissonList.add(permission);
                }
            }
        }
        String[] result = new String[needRequestPermissonList.size()];
        return needRequestPermissonList.toArray(result);
    }

    /**
     * 缓存说明原因的对象
     *
     * @param showCause
     * @param method
     * @param requestCode
     */
    private void putPermissionCause(OnShowCause showCause, Method method, int requestCode) {
        PermissionsMessage message = new PermissionsMessage();
        message.setMethod(method);
        message.setPermissions(showCause.value());
        message.setRequestCode(requestCode);
        mPermissionCause.put(requestCode, message);
    }

    /**
     * 缓存授权成功的对象
     *
     * @param succeeded
     * @param method
     * @param requestCode
     */
    private void putPermissionSucceed(PermissionSucceeded succeeded, Method method, int requestCode) {
        PermissionsMessage message = new PermissionsMessage();
        message.setMethod(method);
        message.setPermissions(succeeded.value());
        message.setRequestCode(requestCode);
        mPermissionSucceed.put(requestCode, message);
    }

    /**
     * 缓存拒绝授权对象
     *
     * @param decline
     * @param method
     * @param requestCode
     */
    private void putPermissionDecline(PermissionDecline decline, Method method, int requestCode) {
        PermissionsMessage message = new PermissionsMessage();
        message.setMethod(method);
        message.setPermissions(decline.value());
        message.setRequestCode(requestCode);
        mPermissionDecline.put(requestCode, message);
    }

    /**
     * 回调
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        PermissionsMessage permissionsMessage = mPermissionSucceed.get(requestCode);
        if (!verifyPermissions(grantResults)) {// 请求权限被拒绝
            PermissionsMessage declineMessage = mPermissionDecline.get(requestCode);
            if (declineMessage != null) {
                invokeMethod(declineMessage.getMethod(), requestCode, permissions, grantResults);
            }
        } else {
            try {
                Method method = permissionsMessage.getMethod();
                method.setAccessible(true);
                method.invoke(mObject, permissionsMessage.getMethodParams());
            } catch (Exception e) {
                Log.e(TAG, "调用函数失败" + e.getMessage());
            }
        }
    }

    /**
     * 注入函数代码
     *
     * @param method
     * @param requestCode
     * @param permissions
     * @param request
     */
    private Object invokeMethod(Method method, int requestCode, String[] permissions, PermissionsRequest request) {
        try {
            method.setAccessible(true);
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (EmptyUtils.isNotEmpty(parameterTypes)) {
                List<Object> params = new ArrayList<Object>();
                for (Class<?> parameterType: parameterTypes) {
                    if (parameterType.equals(Integer.class) || parameterType.equals(int.class)) {
                        params.add(requestCode);
                    } else if (parameterType.equals(PermissionsRequest.class)) {
                        params.add(request);
                    } else if (parameterType.equals(String[].class)) {
                        params.add(permissions);
                    }
                }
                return method.invoke(mObject, params.toArray());
            } else {
                return method.invoke(mObject);
            }
        } catch (Exception e) {
            Log.e(TAG, "invokeMethod失败:" + e.getMessage());
            return null;
        }
    }

    /**
     * 注入函数代码
     *
     * @param method
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    private Object invokeMethod(Method method, int requestCode, String[] permissions, int[] grantResults) {
        try {
            method.setAccessible(true);
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (EmptyUtils.isNotEmpty(parameterTypes)) {
                List<Object> params = new ArrayList<Object>();
                for (Class<?> parameterType: parameterTypes) {
                    if (parameterType.equals(Integer.class) || parameterType.equals(int.class)) {
                        params.add(requestCode);
                    } else if (parameterType.equals(Integer[].class) || parameterType.equals(int[].class)) {
                        params.add(grantResults);
                    } else if (parameterType.equals(String[].class)) {
                        params.add(permissions);
                    }
                }
                return method.invoke(mObject, params.toArray());
            } else {
                return method.invoke(mObject);
            }
        } catch (Exception e) {
            Log.e(TAG, "invokeMethod失败:" + e.getMessage());
            return null;
        }
    }

    /**
     * 检测是否所有的权限都已经授权
     *
     * @param grantResults
     * @return
     */
    private boolean verifyPermissions(int[] grantResults) {
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

}
